#region

using System;
using System.Diagnostics;

#endregion

namespace SharpObservation
{
    /// <summary>
    /// Base class for exposing performant events using the EventHandlerList class.
    /// </summary>
    /// <remarks>
    /// Derive from this class, then implement events as follows:
    /// <code>
    /// private readonly static EventKey&lt;PropertyChangingEventHandler&gt; PropertyChangingEventKey = new EventKey&lt;PropertyChangingEventHandler&gt;();
    /// 
    /// public event PropertyChangingEventHandler PropertyChanging
    /// {
    ///     add { AddHandler(PropertyChangingEventKey, value); }
    ///     remove { RemoveHandler(PropertyChangingEventKey, value); }
    /// }
    /// 
    /// protected void RaiseName()
    /// {
    ///     RaiseEvent(PropertyChangingEventKey, raiseEvent =&gt; raiseEvent(this, new PropertyChangingEventArgs("Name")));
    /// }
    /// </code>
    /// A snippet is available (if installed from Snippets.vsi) to create events, just type: "soevt [tab][tab]"
    /// </remarks>
    public abstract class EventsObject
    {
        /// <summary>
        ///   linked list of delegates
        /// </summary>
        private EventHandlerList _events;

        /// <summary>
        ///   Constructs a new EventsObject
        /// </summary>
        protected EventsObject()
        {
            _events = new EventHandlerList();
        }

        /// <summary>
        ///   Gets whether any delegates are currently attached to any events for this instance.
        /// </summary>
        /// <remarks>
        ///   Results will only be valid for events which were correctly registered using the RegisterEvent method
        /// </remarks>
        public bool IsAnyEventAttached
        {
            get { return _events.IsAnyEventAttached; }
        }

        /// <summary>
        ///   Adds a propertyChanging to the given event
        /// </summary>
        /// <param name = "key">The key used to identify the event</param>
        /// <param name = "value">The delegate to invoke when the event is fired</param>
        [DebuggerStepThrough]
        protected void AddHandler<TDelegate>(EventKey<TDelegate> key, TDelegate value)
        {
            _events.AddHandler(key, value as Delegate);
        }

        /// <summary>
        ///   Removes a propertyChanging from the given event
        /// </summary>
        /// <param name = "key">The key used to identify the event</param>
        /// <param name = "value">The delegate to remove from the firing chain</param>
        [DebuggerStepThrough]
        protected void RemoveHandler<TDelegate>(EventKey<TDelegate> key, TDelegate value)
        {
            _events.RemoveHandler(key, value as Delegate);
        }


        #region RaiseEvent overloads

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate>(EventKey<TDelegate> key, Action<TDelegate> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1>(EventKey<TDelegate>key, TArg1 arg1, Action<TDelegate, TArg1> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <typeparam name="TArg2">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name="arg2">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1, TArg2>(EventKey<TDelegate>key, TArg1 arg1, TArg2 arg2, Action<TDelegate, TArg1, TArg2> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1, arg2);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <typeparam name="TArg2">The type of a user argument</typeparam>
        /// <typeparam name="TArg3">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name="arg2">user defined argument to pass to action</param>
        /// <param name="arg3">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1, TArg2, TArg3>(EventKey<TDelegate>key, TArg1 arg1, TArg2 arg2, TArg3 arg3, Action<TDelegate, TArg1, TArg2, TArg3> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1, arg2, arg3);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <typeparam name="TArg2">The type of a user argument</typeparam>
        /// <typeparam name="TArg3">The type of a user argument</typeparam>
        /// <typeparam name="TArg4">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name="arg2">user defined argument to pass to action</param>
        /// <param name="arg3">user defined argument to pass to action</param>
        /// <param name="arg4">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1, TArg2, TArg3, TArg4>(EventKey<TDelegate>key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, 
                                                                         Action<TDelegate, TArg1, TArg2, TArg3, TArg4> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1, arg2, arg3, arg4);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <typeparam name="TArg2">The type of a user argument</typeparam>
        /// <typeparam name="TArg3">The type of a user argument</typeparam>
        /// <typeparam name="TArg4">The type of a user argument</typeparam>
        /// <typeparam name="TArg5">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name="arg2">user defined argument to pass to action</param>
        /// <param name="arg3">user defined argument to pass to action</param>
        /// <param name="arg4">user defined argument to pass to action</param>
        /// <param name="arg5">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1, TArg2, TArg3, TArg4, TArg5>(EventKey<TDelegate>key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5,
                                                                                Action<TDelegate, TArg1, TArg2, TArg3, TArg4, TArg5> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1, arg2, arg3, arg4, arg5);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <typeparam name="TArg1">The type of a user argument</typeparam>
        /// <typeparam name="TArg2">The type of a user argument</typeparam>
        /// <typeparam name="TArg3">The type of a user argument</typeparam>
        /// <typeparam name="TArg4">The type of a user argument</typeparam>
        /// <typeparam name="TArg5">The type of a user argument</typeparam>
        /// <typeparam name="TArg6">The type of a user argument</typeparam>
        /// <param name = "key">The registered key of the event</param>
        /// <param name="arg1">user defined argument to pass to action</param>
        /// <param name="arg2">user defined argument to pass to action</param>
        /// <param name="arg3">user defined argument to pass to action</param>
        /// <param name="arg4">user defined argument to pass to action</param>
        /// <param name="arg5">user defined argument to pass to action</param>
        /// <param name="arg6">user defined argument to pass to action</param>
        /// <param name = "action">The action handle firing the event if any handlers are attached</param>
        [DebuggerStepThrough]
        protected void RaiseEvent<TDelegate, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(EventKey<TDelegate> key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6,
                                                                                       Action<TDelegate, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6> action)
            where TDelegate : class
        {
            var deleg = _events[key] as TDelegate;

            if (deleg != null)
                action(deleg, arg1, arg2, arg3, arg4, arg5, arg6);
        }

        /// <summary>
        ///   Raises the specified event
        /// </summary>
        /// <typeparam name = "TDelegate">The event callback (Delegate) typeToMatch of the event to raise</typeparam>
        /// <param name = "key">The registered key of the event</param>
        [DebuggerStepThrough]
        protected TDelegate GetEvent<TDelegate>(EventKey<TDelegate> key)
            where TDelegate : class
        {
            return _events[key] as TDelegate;
        }

        #endregion

    }
}